package com.zmm.weblog.admin.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.zmm.weblog.admin.convert.ArticleDetailConvert;
import com.zmm.weblog.admin.model.vo.article.*;
import com.zmm.weblog.admin.service.AdminArticleService;
import com.zmm.weblog.common.domain.dos.*;
import com.zmm.weblog.common.domain.mapper.*;
import com.zmm.weblog.common.enums.ResponseCodeEnum;
import com.zmm.weblog.common.exception.BizException;
import com.zmm.weblog.common.utils.PageResponse;
import com.zmm.weblog.common.utils.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: 福禄寿
 * @Date: 2024/5/14 下午5:05
 * @Des: weblog-springboot
 **/
@Service
@Slf4j
public class AdminArticleServiceImpl implements AdminArticleService {

    @Autowired
    private ArticleCategoryRelMapper articleCategoryRelMapper;

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private ArticleContentMapper articleContentMapper;

    @Autowired
    private ArticleTagRelMapper articleTagRelMapper;

    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private TagMapper tagMapper;

    /**
     * 发布文章
     *
     * @param publishArticleReqVO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Response publishArticle(PublishArticleReqVo publishArticleReqVO) {
        //1.VO 转 ArticleDO,并保存
        ArticleDo articleDO = ArticleDo.builder()
                .title(publishArticleReqVO.getTitle())
                .summary(publishArticleReqVO.getSummary())
                .cover(publishArticleReqVO.getCover())
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now()).build();
        articleMapper.insert(articleDO);
        //拿到插入记录的主键ID
        Long articleId = articleDO.getId();
        //2.VO转ArticleContentDO,并保存
        ArticleContentDo contentDO = ArticleContentDo.builder()
                .articleId(publishArticleReqVO.getCategoryId())
                .content(publishArticleReqVO.getContent()).build();
        articleContentMapper.insert(contentDO);

        //3.处理文章关联的分类
        Long categoryId = publishArticleReqVO.getCategoryId();
        //3.1校验提交的分类是否真实存在
        CategoryDo categoryDo = categoryMapper.selectById(categoryId);
        if (Objects.isNull(categoryDo)) {
            log.warn("==>分类不存在，categoryId:{}", categoryId);
            throw new BizException(ResponseCodeEnum.CATEGORY_NOT_EXISTED);
        }
        ArticleCategoryRelDo articleCategoryRelDO = ArticleCategoryRelDo.builder()
                .articleId(articleId)
                .categoryId(categoryId).build();
        articleCategoryRelMapper.insert(articleCategoryRelDO);
        //4.保存文章关联的标签集合
        List<String> publishTags = publishArticleReqVO.getTags();
        insertTags(articleId, publishTags);

        //5.返回成功
        return Response.success();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Response deleteArticle(Long id) {
        //删除 t_article 中的文章记录；
        articleMapper.deleteById(id);

        //删除 t_article_content 中的内容；
        articleContentMapper.deleteByArticleId(id);

        //删除 t_article_category_rel 表中的文章-分类关联记录；
        articleCategoryRelMapper.deleteByArticleId(id);
        //删除 t_article_tag_rel 表中的文章-标签关联记录；
        articleTagRelMapper.deleteByArticleId(id);

        return Response.success();
    }

    @Override
    public Response findArticlePageList(FindArticlePageListReqVo findArticlePageListReqVo) {
        String title = findArticlePageListReqVo.getTitle();
        LocalDate startDate = findArticlePageListReqVo.getStartDate();
        LocalDate endDate = findArticlePageListReqVo.getEndDate();
        Long size = findArticlePageListReqVo.getSize();
        Long current = findArticlePageListReqVo.getCurrent();
        Page<ArticleDo> page = articleMapper.selectArticlePage(current, size, title, startDate, endDate);

        List<ArticleDo> articleDos = page.getRecords();
        //DO转VO
        List<FindArticlePageListRspVo> vos = null;
        if (!CollectionUtils.isEmpty(articleDos)) {
            vos = articleDos.stream()
                    .map(articleDO -> FindArticlePageListRspVo.builder()
                            .id(articleDO.getId())
                            .title(articleDO.getTitle())
                            .cover(articleDO.getCover())
                            .createTime(articleDO.getCreateTime())
                            .build()).collect(Collectors.toList());
        }

        return PageResponse.success(page, vos);
    }

    @Override
    public Response detailArticle(FindArticleDetailReqVo findArticleDetailReqVo) {
        Long articleId = findArticleDetailReqVo.getId();

        ArticleDo articleDO = articleMapper.selectById(articleId);

        if (Objects.isNull(articleDO)) {
            log.warn("==> 查询的文章不存在，articleId: {}", articleId);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND);
        }

        ArticleContentDo articleContentDO = articleContentMapper.selectByArticleId(articleId);

        // 所属分类
        ArticleCategoryRelDo articleCategoryRelDO = articleCategoryRelMapper.selectByArticleId(articleId);

        // 对应标签
        List<ArticleTagRelDo> articleTagRelDos = articleTagRelMapper.selectByArticleId(articleId);
        // 获取对应标签 ID 集合
        List<Long> tagIds = articleTagRelDos.stream().map(ArticleTagRelDo::getTagId).collect(Collectors.toList());

        // DO 转 VO
        FindArticleDetailRspVo vo = ArticleDetailConvert.INSTANCE.convertDO2VO(articleDO);
        //vo.setContent(articleContentDO.getContent());
        vo.setContent(Objects.isNull(articleContentDO) ? "0" : articleContentDO.getContent());
        vo.setCategoryId(articleCategoryRelDO.getCategoryId());
        vo.setTagIds(tagIds);

        return Response.success(vo);
    }

    @Override
    public Response updateArticle(updateArticleReqVo updateArticleReqVo) {
        Long id = updateArticleReqVo.getId();

        ArticleDo articleDO = ArticleDo.builder()
                .id(id)
                .title(updateArticleReqVo.getTitle())
                .cover(updateArticleReqVo.getCover())
                .summary(updateArticleReqVo.getSummary())
                .updateTime(LocalDateTime.now())
                .build();

        int count = articleMapper.updateById(articleDO);
        if (count == 0) {
            log.warn("==> 更新文章失败，articleId: {}", id);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND);
        }

        //2. VO转ArticleContentDo,并更新
        ArticleContentDo articleContentDO = ArticleContentDo.builder()
                .articleId(id)
                .content(updateArticleReqVo.getContent())
                .build();
        articleContentMapper.updateByArticleId(articleContentDO);

        //3. 更新分类
        Long categoryId = updateArticleReqVo.getCategoryId();

        //3.1校验提交的分类是否真实存在
        CategoryDo categoryDo = categoryMapper.selectById(categoryId);

        if (Objects.isNull(categoryDo)) {
            log.warn("==> 更新文章失败，categoryId: {}", categoryId);
            throw new BizException(ResponseCodeEnum.CATEGORY_NOT_EXISTED);
        }
        //先删除该文章关联的分类记录，再插入新的关联关系
        articleCategoryRelMapper.deleteByArticleId(id);

        ArticleCategoryRelDo articleCategoryRelDO = ArticleCategoryRelDo.builder()
                .articleId(id)
                .categoryId(categoryId)
                .build();

        articleCategoryRelMapper.insert(articleCategoryRelDO);

        //4.保存文章关联的标签集合
        //4.1先删除该文章关联的标签记录，再插入新的关联关系
        articleTagRelMapper.deleteByArticleId(id);

        List<String> tagIds = updateArticleReqVo.getTags();
        insertTags(id, tagIds);
        return Response.success();
    }

    /**
     * 保存标签
     *
     * @param publishTags
     */
    private void insertTags(Long articleId, List<String> publishTags) {
        // TODO
        List<String> existedTags = null;
        List<String> notExistTags;

        //查询出所有标签
        List<TagDo> tagDos = tagMapper.selectList(null);

        //如果表中还没有添加任何标签
        if (CollectionUtils.isEmpty(tagDos)) {
            notExistTags = publishTags;
        } else {
            List<String> tagIds = tagDos.stream().map(tagDo -> String.valueOf(tagDo.getId())).collect(Collectors.toList());
            //表中已添加相关标签，则需要筛选
            //通过标签ID来筛选，包含对于ID则表示提交的标签是表中存在的
            existedTags = publishTags.stream().filter(tagId -> tagIds.contains(tagId)).collect(Collectors.toList());
            //否则是不存在的
            notExistTags = publishTags.stream().filter(tagId -> !tagIds.contains(tagId)).collect(Collectors.toList());

            //按字符串名称提交上来的标签，需要区分大小写
            existedTags = existedTags.stream().map(tagId -> tagId.toLowerCase()).collect(Collectors.toList());

            // 还有一种可能：按字符串名称提交上来的标签，也有可能是表中已存在的，比如表中已经有了 Java 标签，用户提交了个 java 小写的标签，需要内部装换为 Java 标签
            Map<String, Long> tagNameIdMap = tagDos.stream().collect(Collectors.toMap(tagDO -> tagDO.getName().toLowerCase(), TagDo::getId));

            Iterator<String> iterator = notExistTags.iterator();

            while (iterator.hasNext()) {
                String notExistTag = iterator.next();
                //转小写
                if (tagNameIdMap.containsKey(notExistTag.toLowerCase())) {
                    iterator.remove();
                    //如果表中已存在，则需要将表中已存在的标签ID添加到表中
                    existedTags.add(String.valueOf(tagNameIdMap.get(notExistTag.toLowerCase())));
                }
            }
        }

        // 将提交的上来的，已存在于表中的标签，文章-标签关联关系入库
        if (!CollectionUtils.isEmpty(existedTags)) {

            List<ArticleTagRelDo> articleTagRelDos = new ArrayList<>();
            //将标签入库
            existedTags.forEach(tagId -> {
                ArticleTagRelDo articleTagRelDO = ArticleTagRelDo.builder()
                        .articleId(articleId)
                        .tagId(Long.valueOf(tagId))
                        .build();
                articleTagRelDos.add(articleTagRelDO);
            });
            //批量插入
            articleTagRelMapper.insertBatchSomeColumn(articleTagRelDos);
        }

        // 提交上来的，不存在的标签，需要新增标签
        if (!CollectionUtils.isEmpty(notExistTags)) {
            //需要先将标签入库，拿到对应标签 ID 后，再把文章-标签关联关系入库
            List<ArticleTagRelDo> articleTagRelDos = Lists.newArrayList();
            notExistTags.forEach(tagName -> {
                TagDo tagDo = TagDo.builder()
                        .name(tagName)
                        .createTime(LocalDateTime.now())
                        .updateTime(LocalDateTime.now())
                        .build();
                tagMapper.insert(tagDo);

                //拿到保存的标签ID
                Long tagId = tagDo.getId();
                //文章-标签关联关系
                ArticleTagRelDo articleTagRelDO = ArticleTagRelDo.builder().tagId(tagId).articleId(articleId).build();

                articleTagRelDos.add(articleTagRelDO);
            });

            //批量插入
            articleTagRelMapper.insertBatchSomeColumn(articleTagRelDos);
        }

    }

}
